#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Parameter schema definitions for physicochemical and absorption parameters
used in PBPK modeling.
"""

from typing import Dict, Any, List, Optional
from dataclasses import dataclass
import json


@dataclass
class PhysicochemicalParameters:
    """Physical and chemical properties of compounds."""
    molecular_weight: float
    logp: float  # partition coefficient
    pka: float   # acid dissociation constant
    solubility: float  # aqueous solubility
    permeability: float  # intestinal permeability
    
    # Optional properties
    melting_point: Optional[float] = None
    boiling_point: Optional[float] = None
    density: Optional[float] = None
    polar_surface_area: Optional[float] = None
    hydrogen_bond_donors: Optional[int] = None
    hydrogen_bond_acceptors: Optional[int] = None
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary format."""
        return {
            'molecular_weight': self.molecular_weight,
            'logp': self.logp,
            'pka': self.pka,
            'solubility': self.solubility,
            'permeability': self.permeability,
            'melting_point': self.melting_point,
            'boiling_point': self.boiling_point,
            'density': self.density,
            'polar_surface_area': self.polar_surface_area,
            'hydrogen_bond_donors': self.hydrogen_bond_donors,
            'hydrogen_bond_acceptors': self.hydrogen_bond_acceptors,
            'unit': {
                'molecular_weight': 'g/mol',
                'logp': 'unitless',
                'pka': 'unitless',
                'solubility': 'mg/mL',
                'permeability': 'cm/s',
                'melting_point': '°C',
                'boiling_point': '°C',
                'density': 'g/cm³',
                'polar_surface_area': 'Å²',
                'hydrogen_bond_donors': 'count',
                'hydrogen_bond_acceptors': 'count'
            }
        }


@dataclass
class AbsorptionParameters:
    """Absorption-related parameters for PBPK modeling."""
    ka: float  # absorption rate constant
    bioavailability: float  # fraction absorbed (F)
    tlag: float  # lag time
    tmax: float  # time to maximum concentration
    cmax: float  # maximum concentration
    
    # Optional ADME parameters
    fraction_unbound: Optional[float] = None
    clearance: Optional[float] = None
    volume_of_distribution: Optional[float] = None
    half_life: Optional[float] = None
    
    # Transport parameters
    p_gp_substrate: Optional[bool] = None
    p_gp_inhibitor: Optional[bool] = None
    bcrp_substrate: Optional[bool] = None
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary format."""
        return {
            'ka': self.ka,
            'bioavailability': self.bioavailability,
            'tlag': self.tlag,
            'tmax': self.tmax,
            'cmax': self.cmax,
            'fraction_unbound': self.fraction_unbound,
            'clearance': self.clearance,
            'volume_of_distribution': self.volume_of_distribution,
            'half_life': self.half_life,
            'p_gp_substrate': self.p_gp_substrate,
            'p_gp_inhibitor': self.p_gp_inhibitor,
            'bcrp_substrate': self.bcrp_substrate,
            'unit': {
                'ka': '1/h',
                'bioavailability': 'unitless',
                'tlag': 'h',
                'tmax': 'h',
                'cmax': 'ng/mL',
                'fraction_unbound': 'unitless',
                'clearance': 'L/h',
                'volume_of_distribution': 'L',
                'half_life': 'h',
                'p_gp_substrate': 'boolean',
                'p_gp_inhibitor': 'boolean',
                'bcrp_substrate': 'boolean'
            }
        }


class ParameterValidator:
    """Validate parameter values and ranges."""
    
    @staticmethod
    def validate_physicochemical(params: Dict[str, Any]) -> List[str]:
        """Validate physicochemical parameter values."""
        errors = []
        
        if params.get('molecular_weight', 0) <= 0:
            errors.append("Molecular weight must be positive")
        
        if not -10 <= params.get('logp', 0) <= 10:
            errors.append("LogP should be between -10 and 10")
        
        if not 0 <= params.get('pka', 7) <= 14:
            errors.append("pKa should be between 0 and 14")
        
        if params.get('solubility', 0) < 0:
            errors.append("Solubility cannot be negative")
        
        if params.get('permeability', 0) < 0:
            errors.append("Permeability cannot be negative")
        
        return errors
    
    @staticmethod
    def validate_absorption(params: Dict[str, Any]) -> List[str]:
        """Validate absorption parameter values."""
        errors = []
        
        if params.get('ka', 0) < 0:
            errors.append("Absorption rate constant (ka) cannot be negative")
        
        if not 0 <= params.get('bioavailability', 0) <= 1:
            errors.append("Bioavailability must be between 0 and 1")
        
        if params.get('tlag', 0) < 0:
            errors.append("Lag time cannot be negative")
        
        if params.get('tmax', 0) <= 0:
            errors.append("Tmax must be positive")
        
        if params.get('cmax', 0) < 0:
            errors.append("Cmax cannot be negative")
        
        return errors


class ParameterManager:
    """Manage parameter extraction and validation."""
    
    def __init__(self):
        self.physicochemical_schema = PhysicochemicalParameters.__annotations__
        self.absorption_schema = AbsorptionParameters.__annotations__
        self.validator = ParameterValidator()
    
    def create_parameter_template(self) -> Dict[str, Any]:
        """Create a template for expected Excel format."""
        return {
            'physicochemical_template': {
                'columns': [
                    '化合物名称',
                    '分子量',
                    'logP',
                    'pKa',
                    '溶解度',
                    '渗透性',
                    '熔点',
                    '沸点',
                    '密度',
                    '极性表面积',
                    '氢键供体数',
                    '氢键受体数'
                ],
                'units': {
                    '分子量': 'g/mol',
                    'logP': 'unitless',
                    'pKa': 'unitless',
                    '溶解度': 'mg/mL',
                    '渗透性': 'cm/s',
                    '熔点': '°C',
                    '沸点': '°C',
                    '密度': 'g/cm³',
                    '极性表面积': 'Å²',
                    '氢键供体数': 'count',
                    '氢键受体数': 'count'
                }
            },
            'absorption_template': {
                'columns': [
                    '化合物名称',
                    'ka',
                    'F',
                    'Tlag',
                    'Tmax',
                    'Cmax',
                    'fu',
                    'CL',
                    'Vd',
                    't1/2',
                    'P-gp底物',
                    'P-gp抑制剂',
                    'BCRP底物'
                ],
                'units': {
                    'ka': '1/h',
                    'F': 'unitless',
                    'Tlag': 'h',
                    'Tmax': 'h',
                    'Cmax': 'ng/mL',
                    'fu': 'unitless',
                    'CL': 'L/h',
                    'Vd': 'L',
                    't1/2': 'h',
                    'P-gp底物': 'boolean',
                    'P-gp抑制剂': 'boolean',
                    'BCRP底物': 'boolean'
                }
            }
        }
    
    def save_template(self, output_file: str = "data/templates/excel_template.json"):
        """Save the template structure to file."""
        import os
        os.makedirs(os.path.dirname(output_file), exist_ok=True)
        
        template = self.create_parameter_template()
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(template, f, ensure_ascii=False, indent=2)


if __name__ == "__main__":
    manager = ParameterManager()
    template = manager.create_parameter_template()
    print(json.dumps(template, ensure_ascii=False, indent=2))